package com.xtc.netty.io.nio.chat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class NioChatServer {
    private int port = 9090;

    private Charset charset = Charset.forName("UTF-8");

    private static Set<String> users = new HashSet<>();

    public static final String USER_EXIST = "系统提示：该昵称已存在，请换个昵称";

    public static final String USER_CONTENT_SPLIT = "#@#";

    private Selector selector;

    public NioChatServer(int port) throws IOException {
        this.port = port;

        ServerSocketChannel server = ServerSocketChannel.open();
        server.bind(new InetSocketAddress(this.port));
        server.configureBlocking(false);

        selector = Selector.open();

        server.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("服务已启动，监听端口：" + this.port);
    }

    public void listen() throws IOException {
        while (true) {
            int wait = selector.select();
            if (wait == 0) {
                continue;
            }

            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                process(key);
            }
        }
    }

    private void process(SelectionKey key) throws IOException {
        if (key.isAcceptable()) {
            ServerSocketChannel server = (ServerSocketChannel) key.channel();
            SocketChannel client = server.accept();
            client.configureBlocking(false);

            client.register(selector, SelectionKey.OP_READ);

            key.interestOps(SelectionKey.OP_ACCEPT);

            client.write(charset.encode("请输入您的昵称"));
        }

        if (key.isReadable()) {
            SocketChannel client = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            StringBuilder content = new StringBuilder();
            while (client.read(buffer) > 0) {
                buffer.flip();
                content.append(charset.decode(buffer));
            }
            key.interestOps(SelectionKey.OP_READ);

            if (content.length() > 0) {
                String[] arrayContent = content.toString().split(USER_CONTENT_SPLIT);

                // 注册用户
                if (arrayContent != null && arrayContent.length ==1) {
                    String nickName = arrayContent[0];
                    if (users.contains(nickName)) {
                        client.write(charset.encode(USER_EXIST));
                    } else {
                        users.add(nickName);
                        int onlineCount = onlineCount();
                        String message = "欢迎" + nickName + " 进入聊天室，当前在线人数：" + onlineCount;
                        broadCast(null, message);
                    }
                }
                // 注册完了，发送消息
                else if (arrayContent != null && arrayContent.length > 1) {
                    String nickName = arrayContent[0];
                    String message = content.substring(nickName.length() + USER_CONTENT_SPLIT.length());
                    message = nickName + "说 " + message;
                    if (users.contains(nickName)) {
                        broadCast(client, message);
                    }
                }
            }
        }
    }

    /**
     * 广播消息
     *
     * @param client
     * @param message
     * @throws IOException
     */
    private void broadCast(SocketChannel client, String message) throws IOException {
        for (SelectionKey key : selector.keys()) {
            Channel targetChannel = key.channel();
            if (targetChannel instanceof SocketChannel && targetChannel != client) {
                SocketChannel target = (SocketChannel) targetChannel;
                target.write(charset.encode(message));
            }
        }
    }

    private int onlineCount() {
        int res = 0;
        for (SelectionKey key : selector.keys()) {
            Channel target = key.channel();

            if (target instanceof SocketChannel) {
                ++res;
            }
        }
        return res;
    }

    public static void main(String[] args) throws IOException {
        new NioChatServer(9090).listen();
    }
}
